/**
 * Copyright 2019-2022 Huawei Technologies Co., Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "infra/mmpa/mmpa_api.h"

#include <syslog.h>
#include <dirent.h>
#include <net/if.h>
#include <dirent.h>
#include <getopt.h>
#include <libgen.h>

#include <linux/types.h>
#include <linux/hdreg.h>
#include <linux/fs.h>
#include <linux/limits.h>

#include <sys/stat.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/shm.h>
#include <sys/un.h>
#include <sys/utsname.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <sys/statvfs.h>
#include <sys/prctl.h>

#include "securec.h"
#include "infra/base/api_export.h"

#ifdef __cplusplus
#if    __cplusplus
extern "C"{
#endif /* __cpluscplus */
#endif /* __cpluscplus */

/*
 * 描述:获取本地时间
 * 参数: sysTime -- 指向mmSystemTime_t 结构的指针
 * 返回值:执行成功返回EN_OK, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmGetLocalTime(mmSystemTime_t *sysTime)
{
    if (sysTime == NULL) {
        return EN_INVALID_PARAM;
    }

    struct timeval timeVal;
    (void)memset_s(&timeVal, sizeof(timeVal), 0, sizeof(timeVal)); /* unsafe_function_ignore: memset */

    int32_t ret = gettimeofday(&timeVal, NULL);
    if (ret != EN_OK) {
        return EN_ERROR;
    }

    struct tm nowTime;
    (void)memset_s(&nowTime, sizeof(nowTime), 0, sizeof(nowTime)); /* unsafe_function_ignore: memset */

    struct tm *tmp = localtime_r(&timeVal.tv_sec, &nowTime);
    if (tmp == NULL) {
        return EN_ERROR;
    }

    sysTime->wSecond = nowTime.tm_sec;
    sysTime->wMinute = nowTime.tm_min;
    sysTime->wHour = nowTime.tm_hour;
    sysTime->wDay = nowTime.tm_mday;
    sysTime->wMonth = nowTime.tm_mon + 1; // in localtime month is [0, 11], but in fact month is [1, 12]
    sysTime->wYear = nowTime.tm_year + MMPA_COMPUTER_BEGIN_YEAR;
    sysTime->wDayOfWeek = nowTime.tm_wday;
    sysTime->tm_yday = nowTime.tm_yday;
    sysTime->tm_isdst = nowTime.tm_isdst;
    sysTime->wMilliseconds = timeVal.tv_usec / MMPA_ONE_THOUSAND;

    return EN_OK;
}

/*
 * 描述:打开或者创建一个文件
 * 参数: pathName--需要打开或者创建的文件路径名，由用户确保绝对路径
 *       flags--打开或者创建的文件标志位, 默认 user和group的权限
 * 返回值:执行成功返回对应打开的文件描述符, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmOpen(const char *pathName, int32_t flags)
{
    if (pathName == NULL || flags < MMPA_ZERO) {
        return EN_INVALID_PARAM;
    }

    uint32_t tmp = (uint32_t)flags;
    if (((tmp & (O_TRUNC | O_WRONLY | O_RDWR | O_CREAT)) == MMPA_ZERO) && (flags != O_RDONLY)) {
        return EN_INVALID_PARAM;
    }
    // 默认 user和group 有权限
    int32_t fd = open(pathName, flags, S_IRWXU | S_IRWXG);
    if (fd < MMPA_ZERO) {
        return EN_ERROR;
    }
    return fd;
}

/*
 * 描述:打开或者创建一个文件
 * 参数: pathName--需要打开或者创建的文件路径名，由用户确保绝对路径
 *       flags--打开或者创建的文件标志位
 *       mode -- 打开或者创建的权限
 * 返回值:执行成功返回对应打开的文件描述符, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmOpen2(const char *pathName, int32_t flags, MODE mode)
{
    if (pathName == NULL || flags < MMPA_ZERO) {
        return EN_INVALID_PARAM;
    }
    uint32_t tmp = (uint32_t)flags;

    if (((tmp & (O_TRUNC | O_WRONLY | O_RDWR | O_CREAT)) == MMPA_ZERO) && (flags != O_RDONLY)) {
        return EN_INVALID_PARAM;
    }
    if (((mode & (S_IRUSR | S_IREAD)) == MMPA_ZERO) &&
        ((mode & (S_IWUSR | S_IWRITE)) == MMPA_ZERO)) {
        return EN_INVALID_PARAM;
    }

    int32_t fd = open(pathName, flags, mode);
    if (fd < MMPA_ZERO) {
        return EN_ERROR;
    }
    return fd;
}

/*
 * 描述:关闭打开的文件
 * 参数: fd--指向打开文件的资源描述符
 * 返回值:执行成功返回EN_OK, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmClose(int32_t fd)
{
    if (fd < MMPA_ZERO) {
        return EN_INVALID_PARAM;
    }

    int32_t ret = close(fd);
    if (ret != EN_OK) {
        return EN_ERROR;
    }
    return EN_OK;
}

/*
 * 描述:写数据到一个资源文件中
 * 参数: fd--指向打开文件的资源描述符
 *       buf--需要写入的数据
 *       bufLen--需要写入的数据长度
 * 返回值:执行成功返回写入的长度, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmWrite(int32_t fd, const void *buf, uint32_t bufLen)
{
    if (fd < MMPA_ZERO || buf == NULL) {
        return EN_INVALID_PARAM;
    }

    int32_t ret = (int32_t)write(fd, buf, (size_t)bufLen);
    if (ret < MMPA_ZERO) {
        return EN_ERROR;
    }
    return ret;
}

/*
 * 描述:从资源文件中读取数据
 * 参数: fd--指向打开文件的资源描述符
 *       buf--存放读取的数据，由用户分配缓存
 *       bufLen--需要读取的数据大小
 * 返回值:执行成功返回读取的长度, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmRead(int32_t fd, void *buf, uint32_t bufLen)
{
    if (fd < MMPA_ZERO || buf == NULL) {
        return EN_INVALID_PARAM;
    }

    int32_t ret = (int32_t)read(fd, buf, (size_t)bufLen);
    if (ret < MMPA_ZERO) {
        return EN_ERROR;
    }
    return ret;
}

/*
 * 描述:加载一个动态库中的符号
 * 参数:fileName--动态库文件名
 *      mode--打开方式
 * 返回值:执行成功返回动态链接库的句柄, 执行错误返回NULL, 入参检查错误返回NULL
 */
INFRA_API_EXPORT void *mmDlopen(const char *fileName, int32_t mode)
{
    if ((fileName == NULL) || (mode < MMPA_ZERO)) {
        return NULL;
    }

    return dlopen(fileName, mode);
}

/*
 * 描述:获取mmDlopen打开的动态库中的指定符号地址
 * 参数: handle--mmDlopen 返回的指向动态链接库指针
 *       funcName--要求获取的函数的名称
 * 返回值:执行成功返回指向函数的地址, 执行错误返回NULL, 入参检查错误返回NULL
 */
INFRA_API_EXPORT void *mmDlsym(void *handle, const char *funcName)
{
    if ((handle == NULL) || (funcName == NULL)) {
        return NULL;
    }

    return dlsym(handle, funcName);
}

/*
 * 描述:关闭mmDlopen加载的动态库
 * 参数: handle--mmDlopen 返回的指向动态链接库指针
 *       funcName--要求获取的函数的名称
 * 返回值:执行成功返回EN_OK, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmDlclose(void *handle)
{
    if (handle == NULL) {
        return EN_INVALID_PARAM;
    }

    int32_t ret = dlclose(handle);
    if (ret != EN_OK) {
        return EN_ERROR;
    }
    return EN_OK;
}
/*
 * 描述:当mmDlopen动态链接库操作函数执行失败时，mmDlerror可以返回出错信息
 * 返回值:执行成功返回NULL
 */
INFRA_API_EXPORT char *mmDlerror(void)
{
    return dlerror();
}

/*
 * 描述:创建一个目录
 * 参数: pathName -- 需要创建的目录路径名, 比如 char dirName[256]="/home/test";
 *       mode -- 新目录的权限
 * 返回值:执行成功返回EN_OK, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmMkdir(const char *pathName, mmMode_t mode)
{
    if (pathName == NULL) {
        return EN_INVALID_PARAM;
    }

    int32_t ret = mkdir(pathName, mode);
    if (ret != EN_OK) {
        return EN_ERROR;
    }
    return EN_OK;
}

/*
 * 描述:睡眠指定时间
 * 参数: milliSecond -- 睡眠时间 单位ms, linux支持最大支持睡眠时间4294967 ms
 * 返回值:执行成功返回EN_OK, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmSleep(uint32_t milliSecond)
{
    if (milliSecond == MMPA_ZERO) {
        return EN_INVALID_PARAM;
    }
    uint32_t microSecond;

    // 防止截断
    if (milliSecond <= MMPA_MAX_SLEEP_MILLSECOND) {
        microSecond = milliSecond * MMPA_ONE_THOUSAND;
    } else {
        microSecond = 0xffffffff;
    }

    int32_t ret = usleep(microSecond);
    if (ret != EN_OK) {
        return EN_ERROR;
    }
    return EN_OK;
}

/*
 * 描述:判断文件或者目录是否存在
 * 参数: pathName -- 文件路径名
 * 返回值:执行成功返回EN_OK, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmAccess(const char *pathName)
{
    if (pathName == NULL) {
        return EN_INVALID_PARAM;
    }

    int32_t ret = access(pathName, F_OK);
    if (ret != EN_OK) {
        return EN_ERROR;
    }
    return EN_OK;
}

/*
 * 描述:删除目录下所有文件及目录, 包括子目录
 * 参数: pathName -- 目录名全路径
 * 返回值:执行成功返回EN_OK, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmRmdir(const char *pathName)
{
    int32_t ret;
    DIR *childDir = NULL;

    if (pathName == NULL) {
        return EN_INVALID_PARAM;
    }
    DIR *dir = opendir(pathName);
    if (dir == NULL) {
        return EN_INVALID_PARAM;
    }

    struct dirent *entry = NULL;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(".", entry->d_name) == MMPA_ZERO || strcmp("..", entry->d_name) == MMPA_ZERO) {
            continue;
        }
        char buf[PATH_SIZE] = {0};
        ret = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%s/%s", pathName, entry->d_name);
        if (ret == EN_ERROR) {
            break;
        }
        childDir = opendir(buf);
        if (childDir != NULL) {
            (void)closedir(childDir);
            (void)mmRmdir(buf);
            continue;
        } else {
            ret = unlink(buf);
            if (ret == EN_OK) {
                continue;
            }
        }
    }
    (void)closedir(dir);

    ret = rmdir(pathName);
    if (ret == EN_ERROR) {
        return EN_ERROR;
    }
    return EN_OK;
}

/*
 * 描述:获取当前系统时间和时区信息, windows不支持时区获取
 * 参数:timeVal--当前系统时间, 不能为NULL
        timeZone--当前系统设置的时区信息, 可以为NULL, 表示不需要获取时区信息
 * 返回值:执行成功返回EN_OK, 失败返回EN_ERROR，入参错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmGetTimeOfDay(mmTimeval *timeVal, mmTimezone *timeZone)
{
    if (timeVal == NULL) {
        return EN_INVALID_PARAM;
    }
    int32_t ret = gettimeofday((struct timeval *)timeVal, (struct timezone *)timeZone);
    if (ret != EN_OK) {
        ret = EN_ERROR;
    }
    return ret;
}

/*
 * 描述:获取系统开机到现在经过的时间
 * 返回值:执行成功返回类型mmTimespec结构的时间
 */
INFRA_API_EXPORT mmTimespec mmGetTickCount()
{
    mmTimespec rts;
    struct timespec ts = {0};
    (void)clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
    rts.tv_sec = ts.tv_sec;
    rts.tv_nsec = ts.tv_nsec;
    return rts;
}

/*
 * 描述:将参数path所指的相对路径转换成绝对路径, 接口待废弃, 请使用mmRealPath
 * 参数: path--原始路径相对路径
         realPath--规范化后的绝对路径, 由用户分配内存, 长度必须要>= MMPA_MAX_PATH
 * 返回值:执行成功返回EN_OK, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmGetRealPath(char *path, char *realPath)
{
    int32_t ret = EN_OK;
    if (realPath == NULL || path == NULL) {
        return EN_INVALID_PARAM;
    }
    char *pRet = realpath(path, realPath);
    if (pRet == NULL) {
        ret = EN_ERROR;
    }
    return ret;
}

/*
 * 描述:将参数path所指的相对路径转换成绝对路径
 * 参数: path--原始路径相对路径
 *       realPath--规范化后的绝对路径, 由用户分配内存
 *       realPathLen--realPath缓存的长度, 长度必须要>= MMPA_MAX_PATH
 * 返回值:执行成功返回EN_OK, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmRealPath(const char *path, char *realPath, int32_t realPathLen)
{
    int32_t ret = EN_OK;
    if (realPath == NULL || path == NULL || realPathLen < MMPA_MAX_PATH) {
        return EN_INVALID_PARAM;
    }
    char *ptr = realpath(path, realPath);
    if (ptr == NULL) {
        ret = EN_ERROR;
    }
    return ret;
}

/*
 * 描述:返回指定文件流的文件描述符
 * 参数:stream--FILE类型文件流
 * 返回值:执行成功返回EN_OK, 执行错误返回EN_ERROR, 入参检查错误返回EN_INVALID_PARAM
 */
INFRA_API_EXPORT int32_t mmFileno(FILE *stream)
{
    if (stream == NULL) {
        return EN_INVALID_PARAM;
    }

    return fileno(stream);
}

#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cpluscplus */
#endif /* __cpluscplus */

